#ifndef HOMEWORK2_H_INCLUDED
#define HOMEWORK2_H_INCLUDED

#include "BufferManager.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <sys/time.h>
#include "GlobalStuff.h"

const unsigned T_count = 40;
const unsigned QUERYCOUNT = 2500;

struct Trader
{
    int id;
    char name[10];
    double balance;
};

struct Item
{
    int id;
    char name[20];
    int owner;
    int price;
};

template<class T>
class Homework2
{
public:
    void startTest(char const* fileName, const unsigned bufferSize);

protected:

private:
    Trader* createTrader(int traderId);
    Item* createItem(int traderId, int itemId);
    T& prepareTest(const char* fileName, const unsigned bufferSize);
    void transaction(T& manager, int itemId, int buyerId);
    void queryQ1(T& manager);
    void queryQ2(T& manager);
    void createTestFile(const char* fileName, unsigned pageCount);

};

template<class T> void Homework2<T>::startTest(char const* fileName, const unsigned bufferSize)
{
    T& sut = prepareTest(fileName, bufferSize);

    cout << "starting queries and transactions" << endl;

    unsigned tCount = 0, q1Count = 0, q2Count = 0;

    timeval start, end;
    gettimeofday(&start, 0);

    for (unsigned i = 0; i < QUERYCOUNT; i++)
    {
        switch ((rand() % 200))
        {
        case 0:
            queryQ1(sut);
            q1Count++;
            break;
        case 1:
            queryQ2(sut);
            q2Count++;
            break;
        default:
            transaction(sut, (rand() % 1000), (rand() % T_count));
            tCount++;
        }
    }

    gettimeofday(&end, 0);
    cout << "Homework-Test finished: " << (end.tv_sec - start.tv_sec) << " sec"  << endl;
    cout << "transaction: " << tCount << endl;
    cout << "Query1:      " << q1Count << endl;
    cout << "Query2:      " << q2Count << endl;
    cout << "BufferMisses:  " << sut.getBufferMisses() << endl;
    cout << "BufferHits:    " << sut.getBufferHits() << endl;

    delete &sut;
}

/** Set Item.owner to buyerId, deduce Item.price
from buyer's Trader.balance and add the same amount to the previous owner's
balance. Select itemId and buyerId randomly when calling this transaction. */
template<class T> void Homework2<T>::transaction(T& manager, int itemId, int buyerId)
{
    BufferFrame& frame = manager.getPage(T_count + itemId, true);
    Item item;
    char* data = (char*)frame.getData();
    memcpy(&item, data, sizeof(Item));
    double price = item.price;
    int traderId = item.owner;
    item.owner = buyerId;
    memcpy(data, &item, sizeof(Item));

    BufferFrame& frame2 = manager.getPage(traderId, true);
    data = (char*)frame2.getData();
    Trader trader;
    memcpy(&trader, data, sizeof(Trader));
    trader.balance = trader.balance + price;
    memcpy(data, &trader, sizeof(Trader));

    manager.unfixPage(frame, true);
    manager.unfixPage(frame2, true);
}

/** Which trader has the most items? */
template<class T> void Homework2<T>::queryQ1(T& manager)
{
    int traders[T_count] = {0};

    for (unsigned i = 0; i < 1000 * T_count; i++)
    {
        BufferFrame& frame = manager.getPage(T_count + i, true);
        Item item;
        char* data = (char*)frame.getData();
        memcpy(&item, data, sizeof(Item));
        traders[item.owner]++;
        manager.unfixPage(frame, false);
    }

    int maxTrader = 0;
    for (unsigned i = 0; i < T_count; i++)
    {
        if (traders[maxTrader] < traders[i])
        {
            maxTrader = i;
        }
    }
    //cout << maxTrader << endl;
    //now i is the Id of the trader that has the most items
}

/** Which trader is the richest (highest balance)? */
template<class T> void Homework2<T>::queryQ2(T& manager)
{
    int maxTrader = -1;
    double maxBalance = -1;

    for (unsigned i = 0; i < T_count; i++)
    {
        BufferFrame& frame = manager.getPage(i, true);
        Trader trader;
        char* data = (char*)frame.getData();
        memcpy(&trader, data, sizeof(Trader));
        if (maxBalance < trader.balance)
        {
            maxTrader = trader.id;
            maxBalance = trader.balance;
        }
        manager.unfixPage(frame, false);
    }
    //cout << maxTrader << " - " << maxBalance << endl;
    //now maxTrader is the Id of the trader that has the highest balance
}

/**
Creates a Trader with the given Id. All other values are random.
*/
template<class T> Trader* Homework2<T>::createTrader(int traderId)
{
    Trader* trader = new Trader();
    trader->id = traderId;
    trader->balance = rand();
    return trader;
}

/**
Creates a Item with the given Id and the given owner. All other values are random.
*/
template<class T> Item* Homework2<T>::createItem(int traderId, int itemId)
{
    Item* item = new Item();
    item->id = itemId;
    item->owner = traderId;
    return item;
}

template<class T> T& Homework2<T>::prepareTest(const char* fileName, const unsigned bufferSize)
{
    cout << "preparing file for test" << endl;
    createTestFile(fileName, 1001 * T_count);

    cout << "preparing data for test" << endl;
    T* manager = new T(fileName, bufferSize);

    for (unsigned i = 0; i < T_count; i++)
    {
        Trader* trader = createTrader(i);
        BufferFrame& frame = manager->getPage(i, true);
        char* data = (char*)frame.getData();
        char* tp = (char*)trader;
        memcpy(data, tp, sizeof(Trader));
        manager->unfixPage(frame, true);
    }

    for (unsigned i = 0; i < 1000 * T_count; i++)
    {
        Item* item = createItem(i % T_count, i);
        BufferFrame& frame = manager->getPage(T_count + i, true);
        char* data = (char*)frame.getData();
        char* ip = (char*)item;
        memcpy(data, ip, sizeof(Item));
        manager->unfixPage(frame, true);
    }

    return *manager;
}

template<class T> void Homework2<T>::createTestFile(const char* fileName, unsigned pageCount)
{
    //Create TestFile
    FILE* testFile = fopen(fileName, "w");
    char* data = new char[pageCount * pageSize];
    fwrite((void*)data, sizeof(char), pageCount * pageSize, testFile);
    fclose(testFile);
}



#endif // HOMEWORK2_H_INCLUDED
